<script setup lang="ts">
import { Ref, ref, getCurrentInstance, onMounted, onBeforeUnmount, ComponentInternalInstance } from "vue"
import decorationV3 from "comps/decoration/decoration-nymo-v3.vue";
import miniBoxVue from "./miniBox.vue";
import { toolkit, toolsize, createCode } from './drag'
import { removeClass, getItemByClass, styleSetting, checkConfig, findeElement } from './setting'
import { clickMenu, menuOnElement, menuOnEditarea, elementOperate, editAreaOperate } from './menu'


// this
const instance: ComponentInternalInstance | null = getCurrentInstance();
let _this: any = instance ? instance.appContext.config.globalProperties : null;


// sub-Components props
const props = defineProps({
  msg: String
})


// sub-Components emit
const emit = defineEmits(['myclick']);


/**
 * 双向绑定变量
 * @param activeTools 打开的工具箱的ID
 * @param is_menu_on 右键的菜单是否显示
 * @param is_on_element 右键是否点在了组件上
 * @param DRAG 拖拽池
 * @param drag 拖拽元素集合
 * @param config 选中元素的配置项
 */
let activeTools: Ref<number> = ref(0);
let is_menu_on: Ref<boolean> = ref(false);
let is_on_element: Ref<boolean> = ref(false);
let DRAG: Ref<Map<string, any>> = ref(new Map());
let drag: Ref<ArrayObject> = ref({
  div: [],
  button: [],
  card: [],
  carousel: [],
  calendar: [],
  table: [],
});
let config: Ref<StrNumsObject> = ref({
  zIndex: 0,
})



/**
 * 变量
 * @param dragElement 当前拖拽的元素
 * @param copyElement 当前复制的元素
 * @param rightBtnPosition 最新的右键点击坐标
 */
let dragElement: AnyObject | null = null;
let copyElement: AnyObject | null = null;
let rightBtnPosition: StrNumsObject | null = null;



// 移动事件
const ondragging: Function = (ev): void => {

  let canvas: HTMLElement | null = document.getElementById('canvas')

  // 记录落点，用于新增元素到容器中的位置
  if (canvas && dragElement) {
    dragElement.position.left = ev.pageX;
    dragElement.position.top = ev.pageY;
  }
}



// 拖拽事件
const ondrag_start: Function = (ev): void => {
  findeElement(ev, 'tools', (element) => {
    if (element) {
      dragElement = {
        element,
        position: { left: 0, top: 0, }
      };
    }
  })
}



// canvas编辑窗上事件
const ondragover = (ev): void => ev.preventDefault();
const ondrop = (ev): void => {

  // 阻止默认行为
  ev.preventDefault();

  // 获取容器
  let canvas: HTMLElement | null = document.getElementById('canvas')
  if (canvas && dragElement) {

    // 获取编辑区离左上角顶点的距离, 用于计算落点坐标
    let bounds: DOMRect = canvas.getBoundingClientRect()

    // 放入后为容器元素新增监听事件, 用于元素移动、缩放等准备
    // 放入容器后, 元素的移动由监听事件实现，而不用拖拽实现
    let typeInfo: AnyObject = toolsize[dragElement.element.id.substring(15, dragElement.element.id.length)];

    /**
     * @param id id child  当前child的数量 - child的类型 - 组件类别 - 组件类别中的第i个
     * @param width 宽度
     * @param height 高度
     * @param position 位置属性
     * @param left 绝对定位下距离父元素左边的距离
     * @param top 绝对定位下距离父元素上边的距离
     * @param text 元素内部文字
     * @param zIndex 绝对定位下z层级属性
     * @param class 元素的类
     * @param isDrag 正在被拖动?
     * @param is_able: 是否允许拖动
     */
    let newElement: AnyObject = {
      id: `nymo-child${DRAG.value.size}-${typeInfo.type}-${dragElement.element.id.match(/[0-9]\-[0-9]/g)[0]}`,
      width: typeInfo.width,
      height: typeInfo.height,
      position: typeInfo.position,
      left: dragElement.position.left - bounds.x - typeInfo.width / 2,
      top: dragElement.position.top - bounds.y - typeInfo.height / 2,
      text: typeInfo.innerText,
      zIndex: 0,
      class: [],
      isDrag: false,
      is_able: true,
    }

    drag.value[typeInfo.type].push(newElement)
    DRAG.value.set(newElement.id, newElement)
    DRAG.value.set('childNums', DRAG.value.get('childNums') + 1);

    dragElement.element = null;
    dragElement = null;
  }
}




/**
 * @function documentEvent
 * @param none
 * @description 处理编辑区内组件拖拽事件
 */
const documentEvent: Function = (): void => {

  // 鼠标弹起
  document.onmouseup = (ev) => {

    document.onmousemove = null;
    findeElement(ev, 'nymo', (element) => {
      ev.preventDefault()

      // 如果是右键, 打开对应菜单
      if (ev.button === 2) {

        // 保存右键点击的位置
        const canvas: HTMLElement | null = document.getElementById('canvas')
        if (canvas) rightBtnPosition = { x: ev.pageX - canvas.getBoundingClientRect().x, y: ev.pageY - canvas.getBoundingClientRect().y }

        // 点在了组件上
        if (element) {
          is_on_element.value = true
          let dragger: AnyObject = DRAG.value.get(element.id)

          // 添加被选中样式
          removeClass('nymo-selected')
          dragger.class.push('nymo-selected')
        } else {

          // 没有点在组件上
          is_on_element.value = false
        }

        // 打开菜单
        clickMenu(ev, 'clickMenu', 'canvas')
        is_menu_on.value = true
        return
      }

      // 如果是左键
      if (element && ev.button === 0) {
        let dragger: AnyObject = DRAG.value.get(element.id)
        dragger.isDrag = false
        return
      }
    })

  }

  // 鼠标按下
  document.onmousedown = (ev) => {
    findeElement(ev, 'nymo', (element, elementList) => {

      // 关闭菜单
      findeElement(ev, 'menu', (element) => {
        if (!element) is_menu_on.value = false
      })

      // 如果是左键
      if (element && ev.button === 0) {
        ev.preventDefault();

        const canvas: HTMLElement | null = document.getElementById('canvas')
        let dragger: AnyObject = DRAG.value.get(element.id)
        dragger.isDrag = true

        // 添加被选中样式
        removeClass('nymo-selected')
        dragger.class.push('nymo-selected')

        config.value = {
          zIndex: dragger.zIndex
        }

        document.onmousemove = (event) => {

          event.preventDefault();

          // 如果dragger正在被拖动 && 允许拖动 && 找到编辑区 && 左键
          if (dragger.isDrag && dragger.is_able && canvas && event.button === 0) {

            dragger.left = event.clientX - canvas.getBoundingClientRect().x - dragger.width / 2
            dragger.top = event.clientY - canvas.getBoundingClientRect().y - dragger.height / 2
          } else {
            document.onmousemove = null;
          }
        }
      } else {

        // 如果弹起点击编辑区, 清除选中样式
        if (elementList[0].id === 'canvas') {
          removeClass('nymo-selected')
        }
      }
    })
  }
}



// 修改组件配置-外观配置
const fixBtnConfig: Function = (isFixed): void => {
  let { status, msg } = checkConfig(config.value)
  if (!status) return _this.$message({
    message: msg,
    type: 'error'
  })

  let element: Element | null = getItemByClass('nymo-selected', /nymo\-selected/g)
  if (element) {
    let dragger: AnyObject = DRAG.value.get(element.id)

    // 修改
    if (isFixed) {
      dragger.zIndex = config.value.zIndex
      _this.$message({
        message: '修改成功',
        type: 'success'
      })
    }
    config.value = {
      zIndex: isFixed ? dragger.zIndex : 0
    }

  } else _this.$message({
    message: '请先选中组件',
    type: 'error'
  })

}



// 点击菜单的回调
const menuClick: Function = (item: StrNumsObject): void => {
  copyElement = elementOperate(DRAG.value, _this, item, drag.value)
  is_menu_on.value = false
}

// 点击编辑区的回调
const editAreaClick: Function = (item: StrNumsObject): void => {
  editAreaOperate(DRAG.value, drag.value, _this, item, copyElement, rightBtnPosition)
  is_menu_on.value = false
}



// onMounted
onMounted((): void => {

  // 组件数量, 用于id的赋值:主要是复制粘贴
  DRAG.value.set("childNums", 0)

  // 载入功能
  documentEvent()
})

// onBeforeUnmount
onBeforeUnmount((): void => {

  // 解除监听
  DRAG.value.clear()
  document.onmousedown = null
  document.onmousemove = null
  document.onmouseup = null
})

// Exportation
defineExpose({
})
</script>


<template>
  <div id="nymo-drag">
    <!-- 工具列表 -->
    <decorationV3 id="toolkit" class="toolkit-list">
      <template v-slot:chart>
        <div id="toolkit-list-box" ref="toolkit-list-box">
          <el-collapse v-model="activeTools">
            <el-collapse-item v-for="head in toolkit" :name="head.id">
              <template #title>{{ head.title }}</template>
              <miniBoxVue
                :id="`tools-drag-${head.id}-${tools.id}-${tools.type}`"
                v-for="tools in head.subTools"
                :name="tools.name"
                :subTitle="tools.subTitle"
                :draggable="true"
                @dragstart="ondrag_start($event)"
                @drag="ondragging($event)"
                class="miniBox"
              ></miniBoxVue>
            </el-collapse-item>
          </el-collapse>
        </div>
      </template>
    </decorationV3>
    <!-- :style="{ 'display': is_menu_on } "-->
    <!-- 绘制工具的地方 -->
    <div id="canvas" @drop="ondrop($event)" @dragover="ondragover($event)" @contextmenu.prevent>
      <!-- 右键菜单 -->
      <transition name="breadcrumb" mode="out-in">
        <div id="clickMenu" @contextmenu.prevent class="clickMenu" v-show="is_menu_on">
          <div v-show="is_on_element" class="flex flex-column justify-between items-center">
            <el-button
              v-for="item in menuOnElement"
              class="w200px mb-05"
              :type="item.type"
              @click="menuClick(item)"
            >{{ item.text }}</el-button>
          </div>
          <div v-show="!is_on_element" class="flex flex-column justify-between items-center">
            <el-button
              v-for="item in menuOnEditarea"
              class="w200px mb-05"
              :type="item.type"
              @click="editAreaClick(item)"
            >{{ item.text }}</el-button>
          </div>
        </div>
      </transition>
      <!-- 组件库 -->
      <div
        v-for="item in drag.div"
        :id="item.id"
        :class="item.class"
        :style="styleSetting(item)"
      >{{ item.text }}</div>
      <el-button
        v-for="item in drag.button"
        :id="item.id"
        :class="item.class"
        :style="styleSetting(item)"
        type="primary"
      >{{ item.text }}</el-button>
      <el-card
        v-for="item in drag.card"
        :id="item.id"
        :class="item.class"
        :style="styleSetting(item)"
      >{{ item.text }}</el-card>
      <el-carousel
        v-for="item in drag.carousel"
        :id="item.id"
        :class="item.class"
        :style="styleSetting(item)"
      >{{ item.text }}</el-carousel>
      <el-table
        v-for="item in drag.table"
        :id="item.id"
        :class="item.class"
        :style="styleSetting(item)"
      >{{ item.text }}</el-table>
    </div>

    <!-- 组件配置修改处 -->
    <decorationV3 id="fixkit" class="fixkit-list">
      <template v-slot:chart>
        <div id="fixkit-list-box" ref="fixkit-list-box">
          <!-- 配置选项 -->
          <el-collapse>
            <el-collapse-item>
              <template #title>外观配置</template>
              <span class="flex flex-column justify-center items-center">
                z-Index:
                <el-input v-model="config.zIndex" placeholder="z-index"></el-input>
              </span>
              <div class="fixBtn">
                <el-button type @click="fixBtnConfig(false)">默认</el-button>
                <el-button type="primary" @click="fixBtnConfig(true)">修改</el-button>
              </div>
            </el-collapse-item>
            <el-collapse-item>
              <template #title>内容配置</template>
            </el-collapse-item>
          </el-collapse>
          <!-- 底层按钮 -->
          <div class="w100 flex flex-column justify-center items-center">
            <el-button class="w100 mb-1" type="primary">预览</el-button>
            <el-button class="w100" type="primary" @click="createCode(_this, drag)">生成页面代码</el-button>
          </div>
        </div>
      </template>
    </decorationV3>
  </div>
</template>


<style lang="scss" scoped>
@import "styles/mixin.scss";
@mixin style-list-box {
  height: 100%;
  width: 82%;
  padding-left: 3%;
  padding-right: 3%;
  margin: 0 auto;
  position: relative;
  overflow-x: hidden;
  overflow-y: auto;
}
$headH: 2.5rem;
#nymo-drag {
  width: 100%;
  height: 100%;
  position: relative;
  box-shadow: inset 0px 0px 30px
    rgba($color: rgba(241, 241, 241, 0.712), $alpha: 0.4);
  @include flex-row-between-center;
  .toolkit-list {
    margin-left: 1%;
    margin-right: 1%;
    width: 20%;
    height: 100%;
    position: relative;

    #toolkit-list-box {
      @include style-list-box();
    }
    #toolkit-list-box::-webkit-scrollbar {
      width: 5px;
      margin-left: 10px;
      background-color: rgba(0, 0, 0, 0.2);
    }
    #toolkit-list-box::-webkit-scrollbar-track {
      box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
      border-radius: 10px;
    }
    #toolkit-list-box::-webkit-scrollbar-thumb {
      border-radius: 10px;
      box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
      background-color: rgba(148, 148, 148, 0.445);
    }
  }
  .fixkit-list {
    margin-left: 1%;
    margin-right: 1%;
    width: 16%;
    height: 100%;
    position: relative;
    #fixkit-list-box {
      @include style-list-box();
      display: flex;
      flex-direction: column;
      justify-content: space-between;
    }
  }
  #canvas {
    width: 60%;
    height: 95%;
    position: relative;
    box-shadow: inset 0px 0px 30px;
    flex-wrap: wrap;
    overflow: hidden;
    @include flex-row-between-center;
  }
}
.drag-wrapper {
  width: 100px;
  height: 100px;
  box-shadow: inset 0px 0px 30px;
  position: absolute;
}
.miniBox {
  padding-top: 20px;
  padding-bottom: 20px;
  margin-bottom: 1rem;
  cursor: pointer;
}

.clickMenu {
  padding: 1rem 1rem;
  position: absolute;
  box-shadow: inset 0px 0px 40px rgba(230, 230, 230, 0.87);
  border-radius: 5px;
  background-color: rgba(155, 155, 155, 0.295);
  z-index: 9999;
  font-weight: bold;
  @include flex-column-center-center;
}
.fixBtn {
  margin-top: 1rem;
  @include flex-row-between-center;
}
// 设置none, 否则会影响拖拽性能
.el-button {
  transform: none !important;
  transition: none !important;
}

.el-card {
  transform: none !important;
  transition: none !important;
}
.el-button + .el-button {
  margin-right: 0 !important;
  margin-left: 0 !important;
}
</style>
